﻿#pragma once

#include "common.h"
#include <memory>

namespace anip
{
	class Texture2D;
	class Font;
	class RenderBuffer;
	
	class FontManager
	{
		friend class Font;

	private:
		struct Impl;

	public:
		static void init();
		
		static Font get(const string& name, int size);
		static Font get(std::initializer_list<const string> name, int size);

		static void addFace(string name, string file, int index = 0);

		static void finalize();
	};

	class Font
	{
		friend class FontManager;

	private:
		struct Impl;
		std::unique_ptr<Impl> _pimpl;

	public:
		/// <summary>
		/// Creates an empty (null) `Font`.
		/// </summary>
		Font();

		/// <summary>
		/// Copies data from another `Font`.
		/// </summary>
		Font(const Font& obj);

		~Font();

		const Font& operator=(const Font& obj);

		struct CharData
		{
			unsigned char* grayMap = nullptr;
			int width = 0, height = 0;
			int hBearingX = 0, hBearingY = 0, hAdvance = 0;

			~CharData()
			{
				if (grayMap != nullptr)
					delete[] grayMap;
			}
		};

		int& size;

		bool isNull() const;
		bool loadChar(int code, Font::CharData& data) const;
	};

	/*template<>
	void binaryWrite(const Font& f, ostream& out);
	template<>
	void binaryRead(Font& f, istream& in);*/
	
	class TextRenderer
	{
	private:
		static bool _noBreak(wchar_t l, wchar_t r)
		{
			return true; // can break at any time
		}

		static bool _defaultBreak(wchar_t l, wchar_t r)
		{
			static const wstring noStart = L"！？。，…—：”）】}";
			static const wstring noEnd = L"…—【（“{";
			return r == L' ' || l == L'-' || (l > (wchar_t)0x2e80 
				&& noStart.find(r) == string::npos 
				&& noEnd.find(l) == string::npos);
		}

	public:
		typedef bool (*WordBreaker)(wchar_t l, wchar_t r);

		static const WordBreaker noBreak;
		static const WordBreaker defaultBreaker;

		struct Options
		{
			// gcc/clang does not allow aggregates with default member
			// initializers and we must supply a constructor (?)
			Options(){}
			
			int maxWidth = -1;
			int lineSpacing = 5;
			bool wordWrap = true;
			bool ignoreInitialWhitespace = true;
			WordBreaker breaker = nullptr;
			//enum { Left, Right, Center } align = Left;
		};
		
		struct Character
		{
			Font::CharData data{}; // zero-initialization
			wchar_t ch = 0;
			int x = -1, y = -1, col = -1, line = -1;
		};
		
		class StringLayout
		{
			friend class TextRenderer;
		private:
			struct Internal;
			Internal* _internal;
			
			StringLayout(const wstring& _str, const Font& _f, const Options& _opt, float _scale = 1);
			StringLayout(const StringLayout&) = delete;
		public:
			// Returns the size, in pixels, of the bitmap data stored in the string layout
			vec2 dataSize() const;
			// Returns the size used in the drawing system, consistent with the font size used
			// when calling layoutPixelPerfectString()
			vec2 drawingSize() const;
			~StringLayout();
		};

		[[deprecated]]
		static Texture2D* renderString(
			const wstring& str, const Font& f,
			const Options& opt = Options{});

		static StringLayout* layoutPixelPerfectString(
			const wstring& str, const Font& f,
			const Options& opt = Options{}, const RenderBuffer* target = nullptr);

		static StringLayout* layoutString(
			const wstring& str, const Font& f,
			const Options& opt = Options{});

		static Texture2D* renderString(const StringLayout& layout);
		static const Character* getLayout(const StringLayout& layout, int& len);
	};

}